home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Freeware / reportplus / source / f8.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  76.1 KB  |  2,170 lines

  1. #include <exec/types.h>
  2. #include <exec/memory.h>
  3. #include <proto/exec.h>
  4. #include <intuition/intuition.h>
  5. #include <intuition/gadgetclass.h>
  6. #include <proto/intuition.h>
  7. #include <libraries/gadtools.h>
  8. #include <proto/gadtools.h>
  9. #include <dos/dos.h>
  10. #include <dos/exall.h>
  11. #include <dos/dostags.h>
  12. #include <proto/dos.h>
  13. #include <graphics/gfx.h>
  14. #include <clib/graphics_protos.h>
  15. #include <clib/alib_protos.h>
  16. #include <libraries/asl.h>
  17. #include <clib/asl_protos.h>
  18.  
  19. #include <ctype.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "rp.h"
  23.  
  24. #define ALL_REACTION_CLASSES
  25. #include <reaction/reaction.h>
  26.  
  27. #define LIMIT_DIRS        8196
  28. #define MAX_FILES (128 * 1024)
  29. #define TRUNCATE            39
  30. #define BARUPDATES          50
  31. #define GRANULARITY_DIRS    10
  32. #define GRANULARITY_FILES   10
  33.  
  34. /* Limitations:
  35.    LIMIT_DIRS (top-level list entries).
  36.    MAX_FILES (total files/dirs in path). */
  37.  
  38. MODULE void pop(void);
  39. MODULE void push(STRPTR name, ULONG theindex);
  40. MODULE void subdir(ULONG theindex);
  41. MODULE void parent(void);
  42. MODULE void root(void);
  43. MODULE void pathasl(void);
  44. MODULE void comma(ULONG value);
  45. MODULE ABOOL dirasl(void);
  46. MODULE void showduplicates(void);
  47. MODULE void addduplicate(STRPTR path, STRPTR filename);
  48. MODULE void writeline(void);
  49. MODULE void killduplist(void);
  50. MODULE void killsizelist(void);
  51. MODULE void ghost(void);
  52. MODULE void unghost(void);
  53. MODULE void scale(ULONG bytes);
  54. MODULE void __inline progressbar(ULONG level);
  55. MODULE void setbar(ULONG max);
  56.  
  57. #define UNITOPTIONS 3 // counting from 0
  58. MODULE STRPTR UnitOptions[UNITOPTIONS + 1] =
  59. {   "Bytes",
  60.     "Kilobytes",
  61.     "Megabytes",
  62.     "Blocks"
  63. };
  64.  
  65. MODULE struct ColumnInfo SizeColumnInfo[] =
  66. { { 75,            // WORD   ci_Width
  67.     "Pathname",    // STRPTR ci_Title
  68.     0,             // ULONG  ci_Flags
  69.   },
  70.   { 25,
  71.     "Size",
  72.     0, /* Last column must not have CIF_DRAGGABLE set (CIF_DRAGGABLE applies
  73.           to the right border of the relevant column). */
  74.   },
  75.   { -1, (STRPTR) ~0, -1
  76. } },
  77. DupColumnInfo[] =
  78. { { 40,            // WORD   ci_Width
  79.     "Pathname",    // STRPTR ci_Title
  80.     0,             // ULONG  ci_Flags
  81.   },
  82.   { 10,
  83.     "Byte size",
  84.     0,
  85.   },
  86.   { 10,
  87.     "Date",
  88.     0,
  89.   },
  90.   { 10,
  91.     "Time",
  92.     0,
  93.   },
  94.   { 30,
  95.     "Version",
  96.     0, /* Last column must not have CIF_DRAGGABLE set (CIF_DRAGGABLE applies
  97.           to the right border of the relevant column). */
  98.   },
  99.   { -1, (STRPTR) ~0, -1
  100. } };
  101.  
  102. IMPORT SBYTE               page;
  103. IMPORT TEXT                globalname[VLONGFIELD + 1],
  104.                            weekdaystring[10],
  105.                            datestring[10],
  106.                            timestring[9],
  107.                            asldir[VLONGFIELD + 1],
  108.                            aslresult[MEDFIELD + 1],
  109.                            IOBuffer[LONGESTFIELD + 1];
  110. IMPORT struct NewGadget    Gadget;
  111. IMPORT struct SharedStruct shared;
  112. IMPORT struct Screen*      ScreenPtr;
  113. IMPORT struct Window*      MainWindowPtr;
  114. IMPORT struct ExAllData*   EADataPtr;
  115. IMPORT struct Menu*        MenuPtr;
  116. IMPORT Object*             WinObject[FUNCTIONS + 1];
  117.  
  118. AGLOBAL struct Gadget*     size_gadgets[GIDS_8 + 1];
  119.  
  120. MODULE ABOOL               DupNodes       = FALSE,
  121.                            SizeNodes      = FALSE,
  122.                            EmptyDupNodes  = FALSE,
  123.                            EmptySizeNodes = FALSE,
  124.                            ram            = FALSE;
  125. MODULE struct List         SizeList,
  126.                            DupList,
  127.                            EmptySizeList,
  128.                            EmptyDupList;
  129. MODULE STRPTR*             PathnamePtr;
  130. MODULE STRPTR*             FilenamePtr;
  131. MODULE STRPTR*             StackPtr;
  132. MODULE STRPTR*             DirNamePtr;
  133. MODULE ABOOL*              IsDup;
  134. MODULE BPTR                TempHandle     = NULL,
  135.                            DirHandle      = NULL,
  136.                            LogFileHandle  = NULL;
  137. MODULE TEXT                logstring[VLONGFIELD + 1],
  138.                            liststring[5][VLONGFIELD + 1];
  139. MODULE UBYTE               valuestring[11],
  140.                            commastring[14],
  141.                            pathlength;
  142. MODULE ULONG               DirNamesAllocated  = 0,
  143.                            FileNamesAllocated = 0,
  144.                            files              = 0, // how many files have been found. Counting from 1.
  145.                            status             = STATUS_READY,
  146.                            increment;
  147.  
  148. MODULE struct
  149. {   ULONG entries, total, free, capacity, stackentries, blocksize,
  150.           finddup, slack, bytes[LIMIT_DIRS], topdir[LIMIT_DIRS], view,
  151.           numfiles, numdirs;
  152.     LONG  type[LIMIT_DIRS];
  153.     TEXT  oldpath[VLONGFIELD + 1], path[VLONGFIELD + 1];
  154. } size;
  155.  
  156. AGLOBAL void size_init(void)
  157. {   struct Node* ListBrowserNodePtr;
  158.  
  159.     /* This is done for speed. Even though the Version command *might* be
  160.     resident, or *might* fit entirely into the disk buffers, we shouldn't
  161.     count on this being the case. So we copy the Version command into RAM:. */
  162.  
  163.     if (!SystemTags("Copy C:Version RAM:", SYS_Output, Open("NIL:", MODE_NEWFILE), TAG_DONE))
  164.     {   ram = TRUE;
  165.     }
  166.  
  167.     strcpy(size.path, "RAM:");
  168.     size.finddup = size.slack = FALSE;
  169.     size.free = size.capacity = size.total = size.numfiles = size.numdirs = 0;
  170.  
  171.     NewList(&SizeList);
  172.     NewList(&EmptySizeList);
  173.     if (!(ListBrowserNodePtr = AllocListBrowserNode
  174.     (   2,                  // columns
  175.         /* LBNCA_ tags are those that apply to the specific column. */
  176.         LBNA_Column,        0,
  177.         LBNA_Column,        1,
  178.         TAG_END
  179.     )))
  180.     {   rq("Can't create ReAction listbrowser.gadget node(s)!");
  181.     }
  182.     AddTail(&EmptySizeList, ListBrowserNodePtr); // AddTail() has no return code
  183.     EmptySizeNodes = TRUE;
  184.  
  185.     NewList(&DupList);
  186.     NewList(&EmptyDupList);
  187.     if (!(ListBrowserNodePtr = AllocListBrowserNode
  188.     (   5,                  // columns
  189.         /* LBNCA_ tags are those that apply to the specific column. */
  190.         LBNA_Column,        0,
  191.         LBNA_Column,        1,
  192.         LBNA_Column,        2,
  193.         LBNA_Column,        3,
  194.         LBNA_Column,        4,
  195.         TAG_END
  196.     )))
  197.     {   rq("Can't create ReAction listbrowser.gadget node(s)!");
  198.     }
  199.     AddTail(&EmptyDupList, ListBrowserNodePtr); // AddTail() has no return code
  200.     EmptyDupNodes = TRUE;
  201. }
  202.  
  203. AGLOBAL void size1(void)
  204. {   struct Hook         Hook8Struct;
  205.     struct List         ChooserList;
  206.     struct ChooserNode* ChooserNodePtr[UNITOPTIONS + 1];
  207.     ULONG               i;
  208.     TEXT                freestring[13 + 1],
  209.                         totalstring[13 + 1],
  210.                         capacitystring[13 + 1],
  211.                         filesstring[9 + 1],
  212.                         dirsstring[9 + 1],
  213.                         blocksizestring[9 + 1];
  214.  
  215.     comma(size.numfiles);
  216.     strcpy(filesstring, commastring);
  217.     comma(size.numdirs);
  218.     strcpy(dirsstring, commastring);
  219.     comma(size.blocksize);
  220.     strcpy(blocksizestring, commastring);
  221.     scale(size.free);
  222.     strcpy(freestring, commastring);
  223.     scale(size.total);
  224.     strcpy(totalstring, commastring);
  225.     scale(size.capacity);
  226.     strcpy(capacitystring, commastring);
  227.  
  228.     NewList(&ChooserList);
  229.     for (i = 0; i <= UNITOPTIONS; i++)
  230.     {   if (!(ChooserNodePtr[i] = (struct ChooserNode *) AllocChooserNode(CNA_Text, UnitOptions[i], TAG_DONE)))
  231.         {   rq("Can't allocate chooser node!");
  232.         }
  233.         AddTail(&ChooserList, (struct Node *) ChooserNodePtr[i]);
  234.     } // automatically freed by ReAction at DisposeObject() time
  235.  
  236.     /* Create the window object. */
  237.     lockscreen();
  238.     gadtools();
  239.     InitHook(&Hook8Struct, Hook8Func, NULL);
  240.  
  241.     /* We can't just use EmptyList for an empty columnar list, we
  242.     actually need a node (with the LBNA_Column tags) to be present.
  243.  
  244.     LISTBROWSER_ScrollRaster seems not to be usable with
  245.     multi-column lists. */
  246.  
  247.     if (!(WinObject[8] =          NewObject(WINDOW_GetClass(), NULL,
  248.     // window
  249.     WA_PubScreen,                 ScreenPtr,
  250.     WA_ScreenTitle,               "Report+",
  251.     WA_Title,                     "Report+: Path Size Report",
  252.     WA_Activate,                  TRUE,
  253.     WA_DepthGadget,               TRUE,
  254.     WA_DragBar,                   TRUE,
  255.     WA_CloseGadget,               TRUE,
  256.     WA_SizeGadget,                TRUE,
  257.     WA_IDCMP,                     IDCMP_RAWKEY,
  258.     WINDOW_IDCMPHook,             &Hook8Struct,
  259.     WINDOW_IDCMPHookBits,         IDCMP_RAWKEY,
  260.     WINDOW_MenuStrip,             MenuPtr,
  261.     WINDOW_Position,              WPOS_FULLSCREEN,
  262.     WINDOW_ParentGroup,           size_gadgets[GID_8_LY1] =
  263.     NewObject
  264.     (       LAYOUT_GetClass(), NULL,
  265.             // root-layout
  266.             LAYOUT_Orientation,        LAYOUT_ORIENT_VERT,
  267.             LAYOUT_SpaceOuter,         TRUE,
  268.             LAYOUT_DeferLayout,        TRUE,
  269.             LAYOUT_AddChild,
  270.             NewObject
  271.             (   LAYOUT_GetClass(),     NULL,
  272.                 LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
  273.                 LAYOUT_SpaceOuter,     TRUE,
  274.                 LAYOUT_VertAlignment,  LALIGN_CENTER,
  275.                 LAYOUT_HorizAlignment, LALIGN_CENTER,
  276.                 LAYOUT_BevelStyle,     BVS_NONE,
  277.                 LAYOUT_AddImage,
  278.                 NewObject
  279.                 (   LABEL_GetClass(),  NULL,
  280.                     LABEL_Text,        "_Path:",
  281.                     LABEL_Justification,LJ_LEFT,
  282.                 TAG_END),
  283.                 CHILD_WeightedWidth,   0,
  284.                 LAYOUT_AddChild,       size_gadgets[GID_8_ST1] =
  285.                 NewObject
  286.                 (   STRING_GetClass(), NULL,
  287.                     GA_ID,             GID_8_ST1,
  288.                     GA_RelVerify,      TRUE,
  289.                     STRINGA_TextVal,   size.path,
  290.                     STRINGA_MinVisible,35,
  291.                 TAG_END),
  292.                 LAYOUT_AddChild,       size_gadgets[GID_8_BU1] =
  293.                 NewObject(NULL,        "button.gadget",
  294.                     GA_ID,             GID_8_BU1,
  295.                     GA_RelVerify,      TRUE,
  296.                     BUTTON_AutoButton, BAG_POPFILE,
  297.                 TAG_END),
  298.                 CHILD_WeightedWidth,   0,
  299.             TAG_END),
  300.             CHILD_WeightedHeight,      0,
  301.             LAYOUT_AddChild,           NewObject(LAYOUT_GetClass(), NULL,
  302.                 LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
  303.                 LAYOUT_SpaceOuter,     TRUE,
  304.                 LAYOUT_VertAlignment,  LALIGN_CENTER,
  305.                 LAYOUT_HorizAlignment, LALIGN_CENTER,
  306.                 LAYOUT_BevelStyle,     BVS_NONE,
  307.                 LAYOUT_ShrinkWrap,     TRUE,
  308.                 LAYOUT_AddChild,       size_gadgets[GID_8_CB1] =
  309.                 NewObject
  310.                 (   CHECKBOX_GetClass(),NULL,
  311.                     // checkbox
  312.                     GA_ID,             GID_8_CB1,
  313.                     GA_RelVerify,      TRUE,
  314.                     GA_Text,           "_Log to:",
  315.                     GA_Selected,       (BOOL) shared.log,
  316.                     TAG_END
  317.                 ),
  318.                 CHILD_WeightedWidth,   0,
  319.                 LAYOUT_AddChild,       size_gadgets[GID_8_ST2] =
  320.                                        NewObject(STRING_GetClass(), NULL,
  321.                     // string        
  322.                     GA_ID,             GID_8_ST2,
  323.                     GA_RelVerify,      TRUE,
  324.                     STRINGA_TextVal,   shared.output,
  325.                     STRINGA_MinVisible,20,
  326.                     GA_Disabled,       !shared.log,
  327.                     TAG_END
  328.                 ),
  329.                 LAYOUT_AddChild,       size_gadgets[GID_8_BU3] =
  330.                 NewObject
  331.                 (   NULL,              "button.gadget",
  332.                     // button
  333.                     GA_ID,             GID_8_BU3,
  334.                     GA_RelVerify,      TRUE,
  335.                     BUTTON_AutoButton, BAG_POPFILE,
  336.                     GA_Disabled,       !shared.log,
  337.                     TAG_END
  338.                 ),
  339.                 CHILD_WeightedWidth,   0,
  340.                 TAG_END
  341.             ),
  342.             CHILD_WeightedHeight,      0,
  343.             LAYOUT_AddChild,
  344.             NewObject
  345.             (   LAYOUT_GetClass(),     NULL,
  346.                 LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
  347.                 LAYOUT_SpaceOuter,     TRUE,
  348.                 LAYOUT_VertAlignment,  LALIGN_CENTER,
  349.                 LAYOUT_HorizAlignment, LALIGN_CENTER,
  350.                 LAYOUT_BevelStyle,     BVS_FIELD,
  351.                 LAYOUT_AddChild,
  352.                 NewObject
  353.                 (   LAYOUT_GetClass(),     NULL,
  354.                     LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
  355.                     LAYOUT_SpaceOuter,     TRUE,
  356.                     LAYOUT_VertAlignment,  LALIGN_CENTER,
  357.                     LAYOUT_HorizAlignment, LALIGN_CENTER,
  358.                     LAYOUT_BevelStyle,     BVS_NONE,
  359.                     LAYOUT_AddChild,       size_gadgets[GID_8_BU6] =
  360.                     NewObject
  361.                     (   NULL,              "button.gadget",
  362.                         GA_ID,             GID_8_BU6,
  363.                         GA_RelVerify,      TRUE,
  364.                         GA_Text,           "_Root",
  365.                         TAG_END
  366.                     ),
  367.                     CHILD_WeightedWidth,   50,
  368.                     LAYOUT_AddChild,       size_gadgets[GID_8_BU7] =
  369.                     NewObject             
  370.                     (   NULL,              "button.gadget",
  371.                         GA_ID,             GID_8_BU7,
  372.                         GA_RelVerify,      TRUE,
  373.                         GA_Text,           "P_arent",
  374.                         TAG_END
  375.                     ),
  376.                     CHILD_WeightedWidth,   50,
  377.                 TAG_END),
  378.                 CHILD_WeightedHeight,      0,
  379.                 LAYOUT_AddChild,           size_gadgets[GID_8_LB2] =
  380.                 NewObject
  381.                 (   LISTBROWSER_GetClass(),NULL,
  382.                     GA_ID,                 GID_8_LB2,
  383.                     GA_RelVerify,          TRUE,
  384.                     GA_TextAttr,           Gadget.ng_TextAttr,
  385.                     LISTBROWSER_Labels,    (ULONG) &EmptySizeList,
  386.                     LISTBROWSER_ColumnInfo,(ULONG) &SizeColumnInfo,
  387.                     LISTBROWSER_ColumnTitles,TRUE,
  388.                 TAG_END),
  389.                 CHILD_WeightedHeight,      100,
  390.                 LAYOUT_AddChild,
  391.                 NewObject
  392.                 (   LAYOUT_GetClass(),     NULL,
  393.                     LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
  394.                     LAYOUT_SpaceOuter,     TRUE,
  395.                     LAYOUT_VertAlignment,  LALIGN_LEFT,
  396.                     LAYOUT_HorizAlignment, LALIGN_RIGHT,
  397.                     LAYOUT_BevelStyle,     BVS_NONE,
  398.                     LAYOUT_AddChild,
  399.                     NewObject
  400.                     (   LAYOUT_GetClass(),     NULL,
  401.                         LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
  402.                         LAYOUT_SpaceOuter,     TRUE,
  403.                         LAYOUT_VertAlignment,  LALIGN_CENTER,
  404.                         LAYOUT_HorizAlignment, LALIGN_RIGHT,
  405.                         LAYOUT_BevelStyle,     BVS_NONE,
  406.                         LAYOUT_AddImage,
  407.                         NewObject
  408.                         (   LABEL_GetClass(),  NULL,
  409.                             LABEL_Text,        "Files:",
  410.                             LABEL_Justification,LJ_CENTRE,
  411.                         TAG_END),
  412.                         CHILD_WeightedHeight,  0,
  413.                         LAYOUT_AddImage,
  414.                         NewObject
  415.                         (   LABEL_GetClass(),  NULL,
  416.                             LABEL_Text,        "Directories:",
  417.                             LABEL_Justification,LJ_CENTRE,
  418.                         TAG_END),
  419.                         CHILD_WeightedHeight,  0,
  420.                         LAYOUT_AddImage,
  421.                         NewObject
  422.                         (   LABEL_GetClass(),  NULL,
  423.                             LABEL_Text,        "Block size:",
  424.                             LABEL_Justification,LJ_CENTRE,
  425.                         TAG_END),
  426.                         CHILD_WeightedHeight,  0,
  427.                     TAG_END),
  428.                     CHILD_WeightedWidth,       0,
  429.                     LAYOUT_AddChild,
  430.                     NewObject
  431.                     (   LAYOUT_GetClass(),     NULL,
  432.                         LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
  433.                         LAYOUT_SpaceOuter,     TRUE,
  434.                         LAYOUT_VertAlignment,  LALIGN_CENTER,
  435.                         LAYOUT_HorizAlignment, LALIGN_RIGHT,
  436.                         LAYOUT_BevelStyle,     BVS_NONE,
  437.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST6] =
  438.                         NewObject
  439.                         (   STRING_GetClass(), NULL,
  440.                             GA_ID,             GID_8_ST6,
  441.                             GA_ReadOnly,       TRUE,
  442.                             STRINGA_TextVal,   filesstring,
  443.                             STRINGA_MinVisible,13,
  444.                             STRINGA_Justification,GACT_STRINGRIGHT,
  445.                             GA_TextAttr,       Gadget.ng_TextAttr,
  446.                         TAG_END),
  447.                         CHILD_WeightedHeight,  0,
  448.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST7] =
  449.                         NewObject
  450.                         (   STRING_GetClass(), NULL,
  451.                             GA_ID,             GID_8_ST7,
  452.                             GA_ReadOnly,       TRUE,
  453.                             STRINGA_TextVal,   dirsstring,
  454.                             STRINGA_MinVisible,13,
  455.                             STRINGA_Justification,GACT_STRINGRIGHT,
  456.                             GA_TextAttr,       Gadget.ng_TextAttr,
  457.                         TAG_END),
  458.                         CHILD_WeightedHeight,  0,
  459.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST8] =
  460.                         NewObject
  461.                         (   STRING_GetClass(), NULL,
  462.                             GA_ID,             GID_8_ST8,
  463.                             GA_ReadOnly,       TRUE,
  464.                             STRINGA_TextVal,   blocksizestring,
  465.                             STRINGA_MinVisible,13,
  466.                             STRINGA_Justification,GACT_STRINGRIGHT,
  467.                             GA_TextAttr,       Gadget.ng_TextAttr,
  468.                         TAG_END),
  469.                         CHILD_WeightedHeight,  0,
  470.                     TAG_END),
  471.                     CHILD_WeightedWidth,       0,
  472.                     LAYOUT_AddImage,           NewObject
  473.                     (   LABEL_GetClass(),      NULL,
  474.                         LABEL_Text,            "",
  475.                         TAG_END
  476.                     ),
  477.                     CHILD_WeightedWidth,       100,
  478.                     LAYOUT_AddChild,
  479.                     NewObject
  480.                     (   LAYOUT_GetClass(),     NULL,
  481.                         LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
  482.                         LAYOUT_SpaceOuter,     TRUE,
  483.                         LAYOUT_VertAlignment,  LALIGN_CENTER,
  484.                         LAYOUT_HorizAlignment, LALIGN_RIGHT,
  485.                         LAYOUT_BevelStyle,     BVS_NONE,
  486.                         LAYOUT_AddImage,
  487.                         NewObject
  488.                         (   LABEL_GetClass(),  NULL,
  489.                             LABEL_Text,        "Used in path:",
  490.                             LABEL_Justification,LJ_RIGHT,
  491.                         TAG_END),
  492.                         CHILD_WeightedHeight,  0,
  493.                         LAYOUT_AddImage,
  494.                         NewObject
  495.                         (   LABEL_GetClass(),  NULL,
  496.                             LABEL_Text,        "Free on volume:",
  497.                             LABEL_Justification,LJ_RIGHT,
  498.                         TAG_END),
  499.                         CHILD_WeightedHeight,  0,
  500.                         LAYOUT_AddImage,
  501.                         NewObject
  502.                         (   LABEL_GetClass(),  NULL,
  503.                             LABEL_Text,        "Volume capacity:",
  504.                             LABEL_Justification,LJ_RIGHT,
  505.                         TAG_END),
  506.                         CHILD_WeightedHeight,  0,
  507.                     TAG_END),
  508.                     CHILD_WeightedWidth,       0,
  509.                     LAYOUT_AddChild,
  510.                     NewObject
  511.                     (   LAYOUT_GetClass(),     NULL,
  512.                         LAYOUT_Orientation,    LAYOUT_ORIENT_VERT,
  513.                         LAYOUT_SpaceOuter,     TRUE,
  514.                         LAYOUT_VertAlignment,  LALIGN_CENTER,
  515.                         LAYOUT_HorizAlignment, LALIGN_RIGHT,
  516.                         LAYOUT_BevelStyle,     BVS_NONE,
  517.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST4] =
  518.                         NewObject
  519.                         (   STRING_GetClass(), NULL,
  520.                             GA_ID,             GID_8_ST4,
  521.                             GA_ReadOnly,       TRUE,
  522.                             STRINGA_TextVal,   totalstring,
  523.                             STRINGA_MinVisible,13,
  524.                             STRINGA_Justification,GACT_STRINGRIGHT,
  525.                             GA_TextAttr,       Gadget.ng_TextAttr,
  526.                         TAG_END),
  527.                         CHILD_WeightedHeight,  0,
  528.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST5] =
  529.                         NewObject
  530.                         (   STRING_GetClass(), NULL,
  531.                             GA_ID,             GID_8_ST5,
  532.                             GA_ReadOnly,       TRUE,
  533.                             STRINGA_TextVal,   freestring,
  534.                             STRINGA_MinVisible,13,
  535.                             STRINGA_Justification,GACT_STRINGRIGHT,
  536.                             GA_TextAttr,       Gadget.ng_TextAttr,
  537.                         TAG_END),
  538.                         CHILD_WeightedHeight,  0,
  539.                         LAYOUT_AddChild,       size_gadgets[GID_8_ST3] =
  540.                         NewObject
  541.                         (   STRING_GetClass(), NULL,
  542.                             GA_ID,             GID_8_ST3,
  543.                             GA_ReadOnly,       TRUE,
  544.                             STRINGA_TextVal,   capacitystring,
  545.                             STRINGA_MinVisible,13,
  546.                             STRINGA_Justification,GACT_STRINGRIGHT,
  547.                             GA_TextAttr,       Gadget.ng_TextAttr,
  548.                         TAG_END),
  549.                         CHILD_WeightedHeight,  0,
  550.                     TAG_END),
  551.                     CHILD_WeightedWidth,       0,
  552.                 TAG_END),
  553.                 CHILD_WeightedHeight,          0,
  554.             TAG_END),
  555.             CHILD_WeightedHeight,              70,
  556.             LAYOUT_AddChild,
  557.             NewObject
  558.             (   LAYOUT_GetClass(),     NULL,
  559.                 LAYOUT_Orientation,    LAYOUT_ORIENT_HORIZ,
  560.                 LAYOUT_SpaceOuter,     TRUE,
  561.                 LAYOUT_VertAlignment,  LALIGN_CENTER,
  562.                 LAYOUT_HorizAlignment, LALIGN_CENTER,
  563.                 LAYOUT_BevelStyle,     BVS_NONE,
  564.                 LAYOUT_AddImage,
  565.                 NewObject
  566.                 (   LABEL_GetClass(),  NULL,
  567.                     // label
  568.                     LABEL_Text,        "_View as:",
  569.                     LABEL_Justification,LJ_RIGHT,
  570.                 TAG_END),
  571.                 CHILD_WeightedWidth,   0,
  572.                 LAYOUT_AddChild,       size_gadgets[GID_8_CH1] =
  573.                 NewObject
  574.                 (   CHOOSER_GetClass(),NULL,
  575.                     GA_ID,             GID_8_CH1,
  576.                     GA_RelVerify,      TRUE,
  577.                     CHOOSER_PopUp,     TRUE,
  578.                     CHOOSER_Labels,    &ChooserList,
  579.                     CHOOSER_Selected,  size.view,
  580.                     TAG_END
  581.                 ),
  582.                 CHILD_WeightedWidth,   0,
  583.                 LAYOUT_AddImage,       NewObject
  584.                 (   LABEL_GetClass(),  NULL,
  585.                     LABEL_Text,        "",
  586.                     TAG_END
  587.                 ),
  588.                 CHILD_WeightedWidth,   100,
  589.                 LAYOUT_AddChild,       size_gadgets[GID_8_CB2] =
  590.                 NewObject
  591.                 (   CHECKBOX_GetClass(),NULL,
  592.                     // checkbox
  593.                     GA_ID,             GID_8_CB2,
  594.                     GA_RelVerify,      TRUE,
  595.                     GA_Text,           "Include _slack?",
  596.                     GA_Selected,       (BOOL) size.slack,
  597.                 TAG_END),
  598.                 CHILD_WeightedWidth,   0,
  599.                 LAYOUT_AddChild,       size_gadgets[GID_8_CB3] =
  600.                 NewObject
  601.                 (   CHECKBOX_GetClass(),NULL,
  602.                     // checkbox
  603.                     GA_ID,             GID_8_CB3,
  604.                     GA_RelVerify,      TRUE,
  605.                     GA_Text,           "Find _duplicates?",
  606.                     GA_Selected,       (BOOL) size.finddup,
  607.                     TAG_END
  608.                 ),
  609.                 CHILD_WeightedWidth,   0,
  610.             TAG_END),
  611.             CHILD_WeightedHeight,     0,
  612.             LAYOUT_AddChild,          size_gadgets[GID_8_LB1] =
  613.             NewObject
  614.             (   LISTBROWSER_GetClass(),NULL,
  615.                 GA_ID,                GID_8_LB1,
  616.                 GA_ReadOnly,          TRUE,
  617.                 GA_TextAttr,          Gadget.ng_TextAttr,
  618.                 GA_Disabled,          !size.finddup,
  619.                 LISTBROWSER_Labels,   (ULONG) &EmptyDupList,
  620.                 LISTBROWSER_ColumnInfo,(ULONG) &DupColumnInfo,
  621.                 LISTBROWSER_ColumnTitles,TRUE,
  622.                 LISTBROWSER_HorizontalProp,TRUE,
  623.                 LISTBROWSER_VirtualWidth,620 + 155,
  624.             TAG_END),
  625.             CHILD_WeightedHeight,     30,
  626.             LAYOUT_AddChild,          size_gadgets[GID_8_FG1] =
  627.             NewObject
  628.             (   FUELGAUGE_GetClass(), NULL,
  629.                 GA_ID,                GID_8_FG1,
  630.                 GA_Text,              "Ready.",
  631.                 FUELGAUGE_Level,      0,
  632.                 FUELGAUGE_Percent,    FALSE,
  633.                 FUELGAUGE_Justification,FGJ_CENTER,
  634.             TAG_END),
  635.             CHILD_WeightedHeight,     0,
  636.             LAYOUT_AddChild,
  637.             NewObject
  638.             (   LAYOUT_GetClass(),    NULL,
  639.                 LAYOUT_Orientation,   LAYOUT_ORIENT_HORIZ,
  640.                 LAYOUT_SpaceOuter,    TRUE,
  641.                 LAYOUT_VertAlignment, LALIGN_CENTER,
  642.                 LAYOUT_HorizAlignment,LALIGN_CENTER,
  643.                 LAYOUT_BevelStyle,    BVS_NONE,
  644.                 LAYOUT_AddChild,      size_gadgets[GID_8_BU4] =
  645.                 NewObject
  646.                 (   NULL,             "button.gadget",
  647.                     GA_ID,            GID_8_BU4,
  648.                     GA_RelVerify,     TRUE,
  649.                     GA_Text,          "_Update",
  650.                 TAG_END),
  651.                 LAYOUT_AddChild,      size_gadgets[GID_8_BU5] =
  652.                 NewObject
  653.                 (   NULL,             "button.gadget",
  654.                     GA_ID,            GID_8_BU5,
  655.                     GA_RelVerify,     TRUE,
  656.                     GA_Text,          "Stop",
  657.                     GA_Disabled,      TRUE,
  658.                 TAG_END),
  659.             TAG_END),
  660.             CHILD_WeightedHeight,     0,
  661.             LAYOUT_AddChild,          size_gadgets[GID_8_BU2] =
  662.             NewObject
  663.             (   NULL,                 "button.gadget",
  664.                 // button
  665.                 GA_ID,                GID_8_BU2,
  666.                 GA_RelVerify,         TRUE,
  667.                 GA_Text,              "Menu",
  668.             TAG_END),
  669.             CHILD_WeightedHeight,     0,
  670.     TAG_END)
  671.     )))
  672.     {   rq("Can't create ReAction objects!");
  673.     }
  674.     unlockscreen();
  675.     openwindow();
  676.     ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST1]);
  677.  
  678.     loop();
  679.     closewindow();
  680.     size_exit();
  681. }
  682.  
  683. AGLOBAL void updatesize(void)
  684. {   BOOL                  more; // note that it is BOOL, not ABOOL
  685.     TEXT                  tempstring[VLONGFIELD + 1],
  686.                           tempname[LONGFIELD + 1];
  687.     ULONG                 i, j, tempbytes, where;
  688.     LONG                  length, temptype; // these both MUST be signed
  689.     ABOOL                 failed      = FALSE;
  690.     struct FileInfoBlock* FIBPtr      = NULL;
  691.     struct InfoData*      InfoDataPtr;
  692.     struct ExAllControl*  eac;
  693.     struct ExAllData*     ead;
  694.     struct Node*          ListBrowserNodePtr;
  695.  
  696. /* 0: Ready
  697.    1: Reading directory...
  698.  ! 2: Reading subdirectories...
  699.    3: Sorting contents...
  700.  ![4: Matching duplicates...
  701.   [5: Condensing duplicates...
  702.   [6: Sorting duplicates...
  703.  ![7: Examining duplicates...
  704.  
  705. [ = only for duplicates
  706. ! = can be aborted by user
  707.  */
  708.  
  709.     if (SizeNodes)
  710.     {   clearlist(&SizeList);
  711.         SizeNodes = FALSE;
  712.     }
  713.     if (!DirNamePtr)
  714.     {   if (!(DirNamePtr = AllocVec(LIMIT_DIRS * sizeof(STRPTR), NULL)))
  715.         {   rq("Out of memory!");
  716.     }   }
  717.  
  718.     // 1: Ghost relevant gadgets, for duration of the operation.
  719.  
  720.     ghost();
  721.     SetGadgetAttrs
  722.     (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  723.         FUELGAUGE_Min,   0,
  724.         FUELGAUGE_Max,   100,
  725.         FUELGAUGE_Level, 25,
  726.         GA_Text,         "Reading directory...",
  727.         TAG_END
  728.     ); // we don't know how many files in advance...
  729.  
  730.     // 2: Now get a Lock() on the path.
  731.  
  732.     size.stackentries = size.entries = size.numdirs = size.numfiles = 0;
  733.     /* From RKRM I&A, p. 65: */
  734.     if
  735.     (   !(DirHandle = (BPTR) Lock(size.path, ACCESS_READ))
  736.      || !(FIBPtr    = AllocDosObject(DOS_FIB, NULL))
  737.      || !(            Examine(DirHandle, FIBPtr))
  738.      ||  (FIBPtr->fib_DirEntryType <= 0) // if not a directory
  739.     )
  740.     {   failed = TRUE;
  741.     }
  742.  
  743.     strcpy(size.oldpath, size.path);
  744.     if (FIBPtr)
  745.     {   FreeDosObject(DOS_FIB, FIBPtr);
  746.         // FIBPtr = NULL;
  747.     }
  748.     if (failed)
  749.     {   DisplayBeep(ScreenPtr);
  750.         unghost();
  751.  
  752.         strcpy(size.path, size.oldpath);
  753.         SetGadgetAttrs
  754.         (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
  755.             STRINGA_TextVal, size.path,
  756.             TAG_END
  757.         );
  758.         SetGadgetAttrs
  759.         (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  760.             FUELGAUGE_Level, 0,
  761.             GA_Text,         "Failed!",
  762.             TAG_END
  763.         );
  764.         return;
  765.     }
  766.     SetGadgetAttrs
  767.     (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
  768.         STRINGA_TextVal, size.path,
  769.         TAG_END
  770.     );
  771.  
  772.     /* 3: Now we call Info() to ascertain the free bytes.
  773.           InfoDataPtr must be longword-aligned. */
  774.  
  775.     if (!(InfoDataPtr = AllocVec(sizeof(struct InfoData), MEMF_CLEAR)))
  776.     {   rq("Out of memory!");
  777.     }
  778.     if (Info(DirHandle, InfoDataPtr))
  779.     {   size.blocksize = InfoDataPtr->id_BytesPerBlock;
  780.         size.free      =
  781.         (InfoDataPtr->id_NumBlocks - InfoDataPtr->id_NumBlocksUsed) *
  782.          InfoDataPtr->id_BytesPerBlock;
  783.         size.capacity  =
  784.          InfoDataPtr->id_NumBlocks *
  785.          InfoDataPtr->id_BytesPerBlock;
  786.     } else
  787.     {   rq("Can't get volume info!");
  788.     }
  789.     FreeVec(InfoDataPtr);
  790.     // InfoDataPtr = NULL;
  791.  
  792.     comma(size.blocksize);
  793.     SetGadgetAttrs // "dirs"
  794.     (   size_gadgets[GID_8_ST8], MainWindowPtr, NULL,
  795.         STRINGA_TextVal, commastring,
  796.         TAG_END
  797.     );
  798.  
  799.     /* 4: Now we are ready to begin the main part of the operation.
  800.  
  801.     size.topdir[]     = the 'owning' top-level directory for this subdir.
  802.                         Top-level dirs are owned by themselves.
  803.     size.bytes[]      = the size of this top-level directory/file.
  804.     size.entries      = how many top-level dirs, ie. how many names in the
  805.                         listview gadget.
  806.     size.path         = the path (drive, partition, etc.) the user is doing
  807.                         the report on.
  808.     size.stackentries = the current size of the stack.
  809.     DirNamesAllocated = the number of allocated chunks for the
  810.                         topdir names. */
  811.  
  812.     if (!(        StackPtr = AllocVec(LIMIT_DIRS * sizeof(STRPTR), NULL)))
  813.     {   rq("Out of memory!");
  814.     }
  815.  
  816.     files = size.total = 0;
  817.     if (size.finddup)
  818.     {   if (!( PathnamePtr = AllocVec(MAX_FILES * sizeof(STRPTR), NULL)))
  819.         {   rq("Out of memory!");
  820.         }
  821.         if (!( FilenamePtr = AllocVec(MAX_FILES * sizeof(STRPTR), NULL)))
  822.         {   rq("Out of memory!");
  823.         }
  824.         if (!(       IsDup = AllocVec(MAX_FILES * sizeof(ABOOL) , NULL)))
  825.         {   rq("Out of memory!");
  826.     }   }
  827.  
  828.     // This must be allocated with AllocDosObject()
  829.     if (!(eac = AllocDosObject(DOS_EXALLCONTROL, NULL)))
  830.     {   rq("Can't allocate DOS object!");
  831.     }
  832.  
  833.     eac->eac_LastKey = 0;
  834.     do
  835.     {   more = ExAll(DirHandle, (struct ExAllData *) EADataPtr, 4096, ED_SIZE, eac);
  836.         if (!more && IoErr() != ERROR_NO_MORE_ENTRIES)
  837.         {   FreeDosObject(DOS_EXALLCONTROL, eac);
  838.             eac = NULL;
  839.             rq("Can't examine path!");
  840.         }
  841.         if (eac->eac_Entries == 0)
  842.         {   ; /* ExAll() failed normally with no entries */
  843.             continue; /* more is USUALLY zero */
  844.         }
  845.  
  846.         // OK, ExAll() has generated up to 2K of data.
  847.         ead = (struct ExAllData *) EADataPtr;
  848.         do
  849.         {   /* use ead here */
  850.  
  851.             if (ead->ed_Type == -3 || ead->ed_Type == 2) // if a normal file or normal directory (ie. not a link)
  852.             {   size.entries++;
  853.                 if (size.entries == LIMIT_DIRS)
  854.                 {   FreeDosObject(DOS_EXALLCONTROL, eac);
  855.                     eac = NULL;
  856.                     rq("Overflow!");
  857.                 }
  858.                 size.type[size.entries] = ead->ed_Type;
  859.                 if (size.entries > DirNamesAllocated)
  860.                 {   if (!(DirNamePtr[DirNamesAllocated + 1] = AllocMem(VLONGFIELD, MEMF_PUBLIC)))
  861.                     {   FreeDosObject(DOS_EXALLCONTROL, eac);
  862.                         eac = NULL;
  863.                         rq("Out of memory!");
  864.                     }
  865.                     DirNamesAllocated++;
  866.                 }
  867.                 strcpy(DirNamePtr[size.entries], ead->ed_Name);
  868.                 if (ead->ed_Type == 2) /* +2 is dir, +3 is softlink, -3 is file */
  869.                 {   size.numdirs++;
  870.                     if (!(size.numdirs % GRANULARITY_DIRS))
  871.                     {   comma(size.numdirs);
  872.                         SetGadgetAttrs
  873.                         (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
  874.                             STRINGA_TextVal, commastring,
  875.                             TAG_END
  876.                         );
  877.                     }
  878.                     size.bytes[size.entries] = 0;
  879.                     strcpy(tempstring, size.path);
  880.                     if (!AddPart(tempstring, ead->ed_Name, VLONGFIELD))
  881.                     {   FreeDosObject(DOS_EXALLCONTROL, eac);
  882.                         eac = NULL;
  883.                         rq("Can't add filename to pathname!");
  884.                     }
  885.                     push(tempstring, size.entries);
  886.                 } else
  887.                 {   size.numfiles++;
  888.                     if (!(size.numfiles % GRANULARITY_FILES))
  889.                     {   comma(size.numfiles);
  890.                         SetGadgetAttrs
  891.                         (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
  892.                             STRINGA_TextVal, commastring,
  893.                             TAG_END
  894.                         );
  895.                     }
  896.                     addduplicate("", ead->ed_Name);
  897.                     if (size.slack)
  898.                     {   if (ead->ed_Size % size.blocksize == 0)
  899.                         {   size.bytes[size.entries] = (ead->ed_Size / size.blocksize) * size.blocksize;
  900.                             size.total              += (ead->ed_Size / size.blocksize) * size.blocksize;
  901.                         } else
  902.                         {   size.bytes[size.entries] = ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
  903.                             size.total              += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
  904.                     }   }
  905.                     else
  906.                     {   size.bytes[size.entries]     = ead->ed_Size;
  907.                         size.total                  += ead->ed_Size;
  908.             }   }   }
  909.  
  910.             /* get next ead */
  911.             ead = ead->ed_Next;
  912.         } while(ead);
  913.     } while(more);
  914.  
  915.     FreeDosObject(DOS_EXALLCONTROL, eac);
  916.     // eac = NULL;
  917.     UnLock(DirHandle);
  918.     DirHandle = NULL;
  919.  
  920.     SetGadgetAttrs
  921.     (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  922.         FUELGAUGE_Min,   0,
  923.         FUELGAUGE_Max,   100,
  924.         FUELGAUGE_Level, 50,
  925.         GA_Text,         "Reading subdirectories...",
  926.         TAG_END
  927.     );
  928.     pop();
  929.  
  930.     // Now handle the list.
  931.     killsizelist();
  932.     if (status == STATUS_BUSY)
  933.     {   /* size.entries is the actual number of entries. The array elements are
  934.         from 1 to size.entries.
  935.  
  936.         bubble sort routine */
  937.  
  938.         SetGadgetAttrs
  939.         (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  940.             FUELGAUGE_Min,   0,
  941.             FUELGAUGE_Max,   size.entries - 2,
  942.             GA_Text,         "Sorting contents...",
  943.             TAG_END
  944.         );
  945.         setbar(size.entries - 2);
  946.  
  947.         for (i = size.entries; i >= 2; i--)
  948.         {   progressbar(size.entries - 1);
  949.  
  950.             for (j = 2; j <= i; j++)
  951.             {   if (size.bytes[j - 1] < size.bytes[j]) 
  952.                 {   // swap them
  953.                     tempbytes = size.bytes[j - 1];
  954.                     size.bytes[j - 1] = size.bytes[j]; 
  955.                     size.bytes[j] = tempbytes;
  956.                     temptype = size.type[j - 1];
  957.                     size.type[j - 1] = size.type[j];
  958.                     size.type[j] = temptype;
  959.                     strcpy(tempname, DirNamePtr[j - 1]);
  960.                     strcpy(DirNamePtr[j - 1], DirNamePtr[j]);
  961.                     strcpy(DirNamePtr[j], tempname);
  962.         }   }   }
  963.  
  964.         getdate();
  965.         if (shared.log)
  966.         {   strcpy(logstring, "Path: \"");
  967.             strcat(logstring, size.path);
  968.             strcat(logstring, "\" at ");
  969.             strcat(logstring, timestring);
  970.             strcat(logstring, " on ");
  971.             strcat(logstring, weekdaystring);
  972.             strcat(logstring, " ");
  973.             strcat(logstring, datestring);
  974.             strcat(logstring, ":\n");
  975.             if (!(LogFileHandle = (BPTR) Open(shared.output, MODE_READWRITE)))
  976.                 rq("Can't open file for appending!");
  977.             Seek(LogFileHandle, 0, OFFSET_END);
  978.             writeline();
  979.         }
  980.  
  981.         for (i = 1; i <= size.entries; i++)
  982.         {   scale(size.bytes[i]);
  983.  
  984.             if (shared.log)
  985.             {   if (size.type[i] == 2)
  986.                 {   strcpy(logstring, "*");
  987.                 } else
  988.                 {   strcpy(logstring, " ");
  989.                 }
  990.                 strncat(logstring, DirNamePtr[i], TRUNCATE);
  991.                 if (strlen(DirNamePtr[i]) < TRUNCATE)
  992.                 {   length = TRUNCATE - strlen(DirNamePtr[i]);
  993.                     for (j = 1; j <= length; j++)
  994.                     {   strcat(logstring, " ");
  995.                 }   }
  996.                 strcat(logstring, " ");
  997.                 strcat(logstring, commastring);
  998.                 strcat(logstring, "\n");
  999.                 writeline();
  1000.             }
  1001.  
  1002.             if (size.type[i] == 2)
  1003.             {   strcpy(liststring[0], "*");
  1004.             } else
  1005.             {   strcpy(liststring[0], " ");
  1006.             }
  1007.             strcat(liststring[0], DirNamePtr[i]);
  1008.             for (j = 0; j <= 13; j++)
  1009.             {   if (commastring[j] != ' ')
  1010.                 {   where = j; // where is index of first non-space character
  1011.                     break;
  1012.             }   }
  1013.             strcpy(liststring[1], &commastring[where]);
  1014.  
  1015.             if (!(ListBrowserNodePtr = AllocListBrowserNode
  1016.             (   2,                       // columns,
  1017.                 LBNA_Column,             0,
  1018.                     LBNCA_CopyText,      TRUE,
  1019.                     LBNCA_Text,          liststring[0],
  1020.                 LBNA_Column,             1,
  1021.                     LBNCA_CopyText,      TRUE,
  1022.                     LBNCA_Text,          &commastring[where],
  1023.                     LBNCA_Justification, LCJ_RIGHT,
  1024.                 TAG_END
  1025.             )))
  1026.             {   rq("Can't create ReAction listbrowser.gadget node(s)!");
  1027.             }
  1028.  
  1029.             AddTail(&SizeList, ListBrowserNodePtr); // AddTail() has no return code
  1030.             SizeNodes = TRUE;
  1031.         }
  1032.  
  1033.         scale(size.total);
  1034.         SetGadgetAttrs // "used in path"
  1035.         (   size_gadgets[GID_8_ST4], MainWindowPtr, NULL,
  1036.             STRINGA_TextVal, commastring,
  1037.             TAG_END
  1038.         );
  1039.         scale(size.free);
  1040.         SetGadgetAttrs // "free on volume"
  1041.         (   size_gadgets[GID_8_ST5], MainWindowPtr, NULL,
  1042.             STRINGA_TextVal, commastring,
  1043.             TAG_END
  1044.         );
  1045.         scale(size.capacity);
  1046.         SetGadgetAttrs // "volume capacity"
  1047.         (   size_gadgets[GID_8_ST3], MainWindowPtr, NULL,
  1048.             STRINGA_TextVal, commastring,
  1049.             TAG_END
  1050.         );
  1051.         comma(size.numfiles);
  1052.         SetGadgetAttrs // "files"
  1053.         (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
  1054.             STRINGA_TextVal, commastring,
  1055.             TAG_END
  1056.         );
  1057.         comma(size.numdirs);
  1058.         SetGadgetAttrs // "dirs"
  1059.         (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
  1060.             STRINGA_TextVal, commastring,
  1061.             TAG_END
  1062.         );
  1063.  
  1064.         if (shared.log)
  1065.         {   strcpy(logstring, "\nUsed in path:                            ");
  1066.             scale(size.total);
  1067.             strcat(logstring, commastring);
  1068.             strcat(logstring, "\nFree on volume:                          "); // these must be adjusted according to TRUNCATE
  1069.             scale(size.free);
  1070.             strcat(logstring, commastring);
  1071.             strcat(logstring, "\nVolume capacity:                         "); // these must be adjusted according to TRUNCATE
  1072.             scale(size.capacity);
  1073.             strcat(logstring, commastring);
  1074.             strcat(logstring, "\nFiles:                                   "); // these must be adjusted according to TRUNCATE
  1075.             comma(size.numfiles);
  1076.             strcat(logstring, commastring);
  1077.             strcat(logstring, "\nDirectories:                             "); // these must be adjusted according to TRUNCATE
  1078.             comma(size.numdirs);
  1079.             strcat(logstring, commastring);
  1080.             strcat(logstring, "\nBlock size:                              "); // these must be adjusted according to TRUNCATE
  1081.             comma(size.blocksize);
  1082.             strcat(logstring, commastring);
  1083.             strcat(logstring, "\n\n");
  1084.             writeline();
  1085.         }
  1086.  
  1087.         SetGadgetAttrs
  1088.         (   size_gadgets[GID_8_LB2], MainWindowPtr, NULL,
  1089.             LISTBROWSER_Labels, &SizeList,
  1090.             TAG_END
  1091.         );
  1092.         showduplicates();
  1093.     } else
  1094.     {   SetGadgetAttrs // "used in path"
  1095.         (   size_gadgets[GID_8_ST4], MainWindowPtr, NULL,
  1096.             STRINGA_TextVal, "-",
  1097.             TAG_END
  1098.         );
  1099.         SetGadgetAttrs // "free on volume"
  1100.         (   size_gadgets[GID_8_ST5], MainWindowPtr, NULL,
  1101.             STRINGA_TextVal, "-",
  1102.             TAG_END
  1103.         );
  1104.         SetGadgetAttrs // "volume capacity"
  1105.         (   size_gadgets[GID_8_ST3], MainWindowPtr, NULL,
  1106.             STRINGA_TextVal, "-",
  1107.             TAG_END
  1108.         );
  1109.         SetGadgetAttrs // "files"
  1110.         (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
  1111.             STRINGA_TextVal, "-",
  1112.             TAG_END
  1113.         );
  1114.         SetGadgetAttrs // "directories"
  1115.         (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
  1116.             STRINGA_TextVal, "-",
  1117.             TAG_END
  1118.         );
  1119.         SetGadgetAttrs // "block size"
  1120.         (   size_gadgets[GID_8_ST8], MainWindowPtr, NULL,
  1121.             STRINGA_TextVal, "-",
  1122.             TAG_END
  1123.         );
  1124.         killsizelist();
  1125.         killduplist();
  1126.     }
  1127.  
  1128.     unghost();
  1129.     SetGadgetAttrs
  1130.     (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1131.         FUELGAUGE_Min,     0,
  1132.         FUELGAUGE_Max,   100,
  1133.         FUELGAUGE_Level, 100,
  1134.         GA_Text,         "Done",
  1135.         TAG_END
  1136.     );
  1137.  
  1138.     if (LogFileHandle)
  1139.     {   Close(LogFileHandle);
  1140.         LogFileHandle = NULL;
  1141.     }
  1142.     while (FileNamesAllocated)
  1143.     {   FreeMem(StackPtr[FileNamesAllocated], VLONGFIELD);
  1144.         StackPtr[FileNamesAllocated--] = NULL;
  1145.     }
  1146.     FreeVec(StackPtr);
  1147.     StackPtr = NULL;
  1148.  
  1149.     // We need the DirNamePtr[] array to remain valid, because
  1150.     // the user can click on the listview gadget.
  1151. }
  1152.  
  1153. MODULE void subdir(ULONG topdir)
  1154. {   BOOL                 more;
  1155.     TEXT                 tempstring[VLONGFIELD + 1];
  1156.     struct ExAllControl* eac;
  1157.     struct ExAllData*    ead;
  1158.  
  1159.     /* Instead of passing a pointer to the path, we use a global string.
  1160.     There are memory corruption problems if you pass pointers to variables
  1161.     which are really local to other functions.
  1162.  
  1163.     This is dynamically allocated as it must be at least word-aligned;
  1164.     also remember not to allocate large arrays on the stack. */
  1165.  
  1166.     if (!(DirHandle = (BPTR) Lock(globalname, ACCESS_READ)))
  1167.     {   DisplayBeep(ScreenPtr);
  1168.         return;
  1169.     }
  1170.     if (!(eac = AllocDosObject(DOS_EXALLCONTROL, NULL)))
  1171.     {   UnLock(DirHandle);
  1172.         DirHandle = NULL;
  1173.         rq("Can't allocate DOS object!");
  1174.     }
  1175.  
  1176.     eac->eac_LastKey = 0;
  1177.     do
  1178.     {   more = ExAll(DirHandle, (struct ExAllData *) EADataPtr, 4096, ED_SIZE, eac);
  1179.         if ((!more) && (IoErr() != ERROR_NO_MORE_ENTRIES))
  1180.         {   FreeDosObject(DOS_EXALLCONTROL, eac);
  1181.             eac = NULL;
  1182.             UnLock(DirHandle);
  1183.             DirHandle = NULL;
  1184.             rq("Can't examine path!");
  1185.         }
  1186.         if (eac->eac_Entries == 0)
  1187.         {   ; /* ExAll() failed normally with no entries */
  1188.             continue; /* more is USUALLY zero */
  1189.         }
  1190.         ead = (struct ExAllData *) EADataPtr;
  1191.  
  1192.         do
  1193.         {   /* use ead here */
  1194.  
  1195.             if (ead->ed_Type == 2) /* +2 is dir, +3 is softlink, -3 is file */
  1196.             {   size.numdirs++;    
  1197.                 if (!(size.numdirs % GRANULARITY_DIRS))
  1198.                 {   comma(size.numdirs);
  1199.                     SetGadgetAttrs
  1200.                     (   size_gadgets[GID_8_ST7], MainWindowPtr, NULL,
  1201.                         STRINGA_TextVal, commastring,
  1202.                         TAG_END
  1203.                     );
  1204.                 }
  1205.                 strcpy(tempstring, globalname);
  1206.                 if (!AddPart(tempstring, ead->ed_Name, VLONGFIELD))
  1207.                 {   FreeDosObject(DOS_EXALLCONTROL, eac);
  1208.                     eac = NULL;
  1209.                     UnLock(DirHandle);
  1210.                     DirHandle = NULL;
  1211.                     rq("Can't add filename to pathname!");
  1212.                 }
  1213.                 push(tempstring, topdir);
  1214.             } elif (ead->ed_Type == -3) // if it's a file
  1215.             {   size.numfiles++;
  1216.                 if (!(size.numfiles % GRANULARITY_FILES))
  1217.                 {   comma(size.numfiles);
  1218.                     SetGadgetAttrs
  1219.                     (   size_gadgets[GID_8_ST6], MainWindowPtr, NULL,
  1220.                         STRINGA_TextVal, commastring,
  1221.                         TAG_END
  1222.                     );
  1223.                 }
  1224.                 addduplicate(&globalname[pathlength], ead->ed_Name);
  1225.                 if (size.slack)
  1226.                 {   if (ead->ed_Size % size.blocksize == 0)
  1227.                     {   size.bytes[topdir] += (ead->ed_Size / size.blocksize) * size.blocksize;
  1228.                         size.total         += (ead->ed_Size / size.blocksize) * size.blocksize;
  1229.                     } else
  1230.                     {   size.bytes[topdir] += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
  1231.                         size.total         += ((ead->ed_Size / size.blocksize) + 1) * size.blocksize;
  1232.                 }   }
  1233.                 else
  1234.                 {   size.bytes[topdir]     += ead->ed_Size;
  1235.                     size.total             += ead->ed_Size;
  1236.             }   }
  1237.  
  1238.             /* get next ead */
  1239.             ead = ead->ed_Next;
  1240.         } while(ead);
  1241.     } while(more);
  1242.  
  1243.     FreeDosObject(DOS_EXALLCONTROL, eac);
  1244.     // eac = NULL;
  1245.     UnLock(DirHandle);
  1246.     DirHandle = NULL;
  1247.     // don't call pop() yourself!
  1248. }
  1249.  
  1250. MODULE void pop(void)
  1251. {   ULONG breakval;
  1252.  
  1253.     pathlength = strlen(size.path);
  1254.     while (size.stackentries && status == STATUS_BUSY)
  1255.     {   size.stackentries--;
  1256.         strcpy(globalname, StackPtr[size.stackentries + 1]);
  1257.         subdir(size.topdir[size.stackentries + 1]);
  1258.         breakval = ra_checkbreak();
  1259.         if (breakval == 2)
  1260.             cleanexit(EXIT_SUCCESS);
  1261.         elif (breakval == 1)
  1262.             status = STATUS_STOPPING;
  1263. }   }
  1264. MODULE void push(STRPTR name, ULONG theindex)
  1265. {   size.stackentries++;
  1266.     if (size.stackentries == LIMIT_DIRS)
  1267.         rq("Overflow!");
  1268.     if (size.stackentries > FileNamesAllocated)
  1269.     {   if (!(StackPtr[FileNamesAllocated + 1] = AllocMem(VLONGFIELD, MEMF_PUBLIC)))
  1270.             rq("Out of memory!");
  1271.         FileNamesAllocated++;
  1272.     }
  1273.     strcpy(StackPtr[size.stackentries], name);
  1274.     size.topdir[size.stackentries] = theindex;
  1275. }
  1276.  
  1277. AGLOBAL void size_loop(ULONG gid)
  1278. {   ULONG  code;
  1279.     STRPTR stringptr;
  1280.  
  1281.     switch(gid)
  1282.     {
  1283.     case GID_8_BU1: // path ASL
  1284.         pathasl();
  1285.     break;
  1286.     case GID_8_BU2: // menu
  1287.         page = 0;
  1288.     break;
  1289.     case GID_8_BU3:
  1290.         outputasl("~(#?.info)");
  1291.     break;
  1292.     case GID_8_BU4: // update
  1293.         updatesize();
  1294.     break;
  1295.     // GID_8_BU5 is stop
  1296.     case GID_8_BU6:
  1297.         root();
  1298.     break;
  1299.     case GID_8_BU7:
  1300.         parent();
  1301.     break;
  1302.     case GID_8_ST1: // path string
  1303.         if (!(GetAttr
  1304.         (   STRINGA_TextVal, size_gadgets[GID_8_ST1], (ULONG *) &stringptr
  1305.         )))
  1306.         {   rq("Unsupported inquiry!"); // should never happen
  1307.         }
  1308.         strcpy(size.path, stringptr);
  1309.         updatesize();
  1310.     break;
  1311.     case GID_8_ST2: // output string
  1312.         if (!(GetAttr
  1313.         (   STRINGA_TextVal, size_gadgets[GID_8_ST2], (ULONG *) &stringptr
  1314.         )))
  1315.         {   rq("Unsupported inquiry!"); // should never happen
  1316.         }
  1317.         strcpy(shared.output, stringptr);
  1318.     break;
  1319.     case GID_8_CB1:
  1320.         if (!(GetAttr
  1321.         (   GA_Selected, size_gadgets[GID_8_CB1], &shared.log
  1322.         )))
  1323.         {   rq("Unsupported inquiry!"); // should never happen
  1324.         }
  1325.  
  1326.         SetGadgetAttrs // `Log to file:' (string)
  1327.         (   size_gadgets[GID_8_ST2], MainWindowPtr, NULL,
  1328.             GA_Disabled, !shared.log,
  1329.             TAG_END
  1330.         );
  1331.         /* For some reason, we need to explicitly refresh the string gadget
  1332.         visuals here */
  1333.         RefreshGadgets((struct Gadget *) size_gadgets[GID_8_ST2], MainWindowPtr, NULL);
  1334.  
  1335.         SetGadgetAttrs // `Log to file:' (ASL button)
  1336.         (   size_gadgets[GID_8_BU3], MainWindowPtr, NULL,
  1337.             GA_Disabled, !shared.log,
  1338.             TAG_END
  1339.         );
  1340.         if (shared.log)
  1341.         {   ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST2]);
  1342.         }
  1343.     break;
  1344.     case GID_8_CB2:
  1345.         if (!(GetAttr
  1346.         (   GA_Selected, size_gadgets[GID_8_CB2], (ULONG *) &size.slack
  1347.         )))
  1348.         {   rq("Unsupported inquiry!"); // should never happen
  1349.         }
  1350.     break;
  1351.     case GID_8_CB3:
  1352.         if (!(GetAttr
  1353.         (   GA_Selected, size_gadgets[GID_8_CB3], (ULONG *) &size.finddup
  1354.         )))
  1355.         {   rq("Unsupported inquiry!"); // should never happen
  1356.         }
  1357.         SetGadgetAttrs
  1358.         (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
  1359.             GA_Disabled, !size.finddup,
  1360.             TAG_END
  1361.         );
  1362.     break;
  1363.     case GID_8_CH1:
  1364.         if (!(GetAttr
  1365.         (   CHOOSER_Selected, size_gadgets[GID_8_CH1], (ULONG *) &size.view
  1366.         )))
  1367.         {   rq("Unsupported inquiry!"); // should never happen
  1368.         }
  1369.     break;
  1370.     case GID_8_LB2:
  1371.         /* code is the position within the list, starting from 0. */
  1372.  
  1373.         if (!(GetAttr
  1374.         (   LISTBROWSER_Selected, size_gadgets[GID_8_LB2], (ULONG *) &code
  1375.         )))
  1376.         {   rq("Unsupported inquiry!"); // should never happen
  1377.         }
  1378.         code++;
  1379.         if (code <= size.entries && size.type[code] == 2)
  1380.         {   if (!(AddPart(size.path, DirNamePtr[code], VLONGFIELD + 1)))
  1381.             {   rq("Can't add filename to pathname!");
  1382.             } else
  1383.             {   updatesize();
  1384.         }   }
  1385.     break;
  1386.     default:
  1387.     break;
  1388. }   }
  1389.  
  1390. AGLOBAL void size_exit(void)
  1391. {   if (TempHandle)
  1392.     {   UnLock(TempHandle);
  1393.         TempHandle = NULL;
  1394.     }
  1395.     if (DirHandle)
  1396.     {   UnLock(DirHandle);
  1397.         DirHandle = NULL;
  1398.     }
  1399.     if (LogFileHandle)
  1400.     {   Close(LogFileHandle);
  1401.         LogFileHandle = NULL;
  1402.     }
  1403.  
  1404.     // Only call this if the list gadget is detached or not present.
  1405.     // (ie. after closewindow()).
  1406.     if (SizeNodes)
  1407.     {   clearlist(&SizeList);
  1408.         SizeNodes = FALSE;
  1409.     }
  1410.     if (DupNodes)
  1411.     {   clearreactionlist(&DupList);
  1412.         DupNodes = FALSE;
  1413.     }
  1414.     while (FileNamesAllocated)
  1415.     {   FreeMem(StackPtr[FileNamesAllocated], VLONGFIELD);
  1416.         StackPtr[FileNamesAllocated--] = NULL;
  1417.     }
  1418.     if (StackPtr)
  1419.     {   FreeVec(StackPtr);
  1420.         StackPtr = NULL;
  1421.     }
  1422.     while ( DirNamesAllocated)
  1423.     {   FreeMem(DirNamePtr[DirNamesAllocated], VLONGFIELD);
  1424.         DirNamePtr[DirNamesAllocated--] = NULL;
  1425.     }
  1426.     if (DirNamePtr)
  1427.     {   FreeVec(DirNamePtr);
  1428.         DirNamePtr = NULL;
  1429. }   }
  1430.  
  1431. AGLOBAL void size_die(void)
  1432. {   if (EmptySizeNodes)
  1433.     {   clearreactionlist(&EmptySizeList);
  1434.         EmptySizeNodes = FALSE;
  1435.     }
  1436.     if (EmptyDupNodes)
  1437.     {   clearreactionlist(&EmptyDupList);
  1438.         EmptyDupNodes = FALSE;
  1439.     }
  1440.     IOBuffer[6]  = size.finddup;
  1441.     IOBuffer[21] = size.slack;
  1442.     IOBuffer[22] = (UBYTE) size.view;
  1443.  
  1444.     if (ram)
  1445.     {   if (!SystemTags("Delete RAM:Version", SYS_Output, Open("NIL:", MODE_NEWFILE), TAG_DONE))
  1446.         {   ram = FALSE;
  1447. }   }   }
  1448.  
  1449. AGLOBAL void size_config(void)
  1450. {   size.finddup = IOBuffer[6];
  1451.     size.slack   = IOBuffer[21];
  1452.     size.view    = (SWORD) IOBuffer[22];
  1453. }
  1454.  
  1455. MODULE void parent(void)
  1456. {   BPTR DirHandle,
  1457.          ParentHandle;
  1458.  
  1459.     if (!(DirHandle = (BPTR) Lock(size.path, ACCESS_READ)))
  1460.         rq("Can't lock directory!");
  1461.     if (!(ParentHandle = ParentDir(DirHandle)))
  1462.     {   /* It returned a NULL lock; ie. a lock on SYS:
  1463.         That is not what we want. */
  1464.         ParentHandle = DirHandle;
  1465.     }
  1466.     if (!NameFromLock(ParentHandle, size.path, VLONGFIELD))
  1467.         rq("Can't get name from lock!");
  1468.     UnLock(DirHandle);
  1469.     // DirHandle = NULL;
  1470.  
  1471.     updatesize();
  1472. }
  1473.  
  1474. MODULE void root(void)
  1475. {   BPTR  DirHandle,
  1476.           ParentHandle;
  1477.     ABOOL rootdone     = FALSE;
  1478.  
  1479.     if (!(DirHandle = (BPTR) Lock(size.path, ACCESS_READ)))
  1480.         rq("Can't lock directory!");
  1481.     do
  1482.     {   if (!(ParentHandle = ParentDir(DirHandle)))
  1483.         {   /* It returned a NULL lock; ie. a lock on SYS:
  1484.                That is not what we want. */
  1485.             ParentHandle = DirHandle;
  1486.             rootdone = TRUE;
  1487.         } else DirHandle = ParentHandle;
  1488.     } while (!rootdone);
  1489.     if (!NameFromLock(ParentHandle, size.path, VLONGFIELD))
  1490.         rq("Can't get name from lock!");
  1491.     UnLock(DirHandle);
  1492.     // DirHandle = NULL;
  1493.  
  1494.     updatesize();
  1495. }
  1496.  
  1497. MODULE void pathasl(void)
  1498. {   strcpy(asldir, size.path);
  1499.     if (dirasl()) // path for ASL must be valid?
  1500.     {   strcpy(size.path, aslresult);
  1501.         SetGadgetAttrs
  1502.         (   size_gadgets[GID_8_ST1], MainWindowPtr, NULL,
  1503.             STRINGA_TextVal, size.path,
  1504.             TAG_END
  1505.         );
  1506.         updatesize();
  1507. }   }
  1508.  
  1509. MODULE void comma(ULONG value)
  1510. {   ABOOL yes = FALSE;
  1511.  
  1512.     strcpy(commastring, " ,   ,   ,   ");
  1513.     //                    1   5   9
  1514.  
  1515.     valuestring[ 0] = '0' + (value / 1000000000);
  1516.     value %= 1000000000;
  1517.     if (valuestring[ 0] != '0')
  1518.     {   commastring[0] = valuestring[0];
  1519.         yes = TRUE;
  1520.     } else
  1521.     {   commastring[ 0] = ' ';
  1522.         commastring[ 1] = ' ';
  1523.     }
  1524.  
  1525.     valuestring[ 1] = '0' + (value /  100000000);
  1526.     value %=  100000000;
  1527.     if (yes || valuestring[1] != '0')
  1528.     {   commastring[2] = valuestring[1];
  1529.         yes = TRUE;
  1530.     } else commastring[2] = ' ';
  1531.  
  1532.     valuestring[ 2] = '0' + (value /   10000000);
  1533.     value %=   10000000;
  1534.     if (yes || valuestring[2] != '0')
  1535.     {   commastring[3] = valuestring[2];
  1536.         yes = TRUE;
  1537.     } else commastring[3] = ' ';
  1538.  
  1539.     valuestring[ 3] = '0' + (value /    1000000);
  1540.     value %=    1000000;
  1541.     if (yes || valuestring[3] != '0')
  1542.     {   commastring[4] = valuestring[3];
  1543.         yes = TRUE;
  1544.     } else
  1545.     {   commastring[4] = ' ';
  1546.         commastring[5] = ' ';
  1547.     }
  1548.  
  1549.     valuestring[ 4] = '0' + (value /     100000);
  1550.     value %=     100000;
  1551.     if (yes || valuestring[4] != '0')
  1552.     {   commastring[6] = valuestring[4];
  1553.         yes = TRUE;
  1554.     } else commastring[6] = ' ';
  1555.  
  1556.     valuestring[ 5] = '0' + (value /      10000);
  1557.     value %=      10000;
  1558.     if (yes || valuestring[5] != '0')
  1559.     {   commastring[7] = valuestring[5];
  1560.         yes = TRUE;
  1561.     } else commastring[7] = ' ';
  1562.  
  1563.     valuestring[ 6] = '0' + (value /       1000);
  1564.     value %=       1000;
  1565.     if (yes || valuestring[6] != '0')
  1566.     {   commastring[8] = valuestring[6];
  1567.         yes = TRUE;
  1568.     } else
  1569.     {   commastring[8] = ' ';
  1570.         commastring[9] = ' ';
  1571.     }
  1572.  
  1573.     valuestring[ 7] = '0' + (value /        100);
  1574.     value %=        100;
  1575.     if (yes || valuestring[7] != '0')
  1576.     {   commastring[10] = valuestring[7];
  1577.         yes = TRUE;
  1578.     } else commastring[10] = ' ';
  1579.  
  1580.     valuestring[ 8] = '0' + (value /         10);
  1581.     value %=         10;
  1582.     if (yes || valuestring[8] != '0')
  1583.     {   commastring[11] = valuestring[8];
  1584.         // yes = TRUE;
  1585.     } else commastring[11] = ' ';
  1586.  
  1587.     valuestring[ 9] = '0' +  value              ;
  1588.     commastring[12] = valuestring[9];
  1589. }
  1590.  
  1591. MODULE ABOOL dirasl(void)
  1592. {   struct FileRequester* ASLRqPtr;
  1593.     ABOOL                 success;
  1594.  
  1595.     if (!(ASLRqPtr = AllocAslRequestTags(ASL_FileRequest, ASL_Pattern, "~(#?.info)", ASL_Window, MainWindowPtr, ASL_ExtFlags1, FIL1F_NOFILES, TAG_DONE)))
  1596.     {   rq("Can't create ASL request!");
  1597.     }
  1598.     if (AslRequestTags(ASLRqPtr, ASL_Dir, asldir, ASL_Hail, "Report+ path selector", ASL_FuncFlags, FILF_PATGAD, TAG_DONE))
  1599.     {   strcpy(aslresult, ASLRqPtr->rf_Dir);
  1600.         success = TRUE;
  1601.     } else success = FALSE;
  1602.     FreeAslRequest(ASLRqPtr);
  1603.     return(success);
  1604. }
  1605.  
  1606. MODULE void showduplicates(void)
  1607. {   ABOOL                 found = FALSE,
  1608.                           started = FALSE,
  1609.                           success;
  1610.     TEXT                  pathnamestring[VLONGFIELD + 1],
  1611.                           commandstring[VLONGFIELD + 1],
  1612.                           lastfilename[VLONGFIELD + 1];
  1613.     ULONG                 breakval, i, j, k, where,
  1614.                           firstpen, secondpen, currentpen, dups;
  1615.     LONG                  length;
  1616.     STRPTR                tempPathnamePtr, tempFilenamePtr;
  1617.     struct DateTime       DateTime;
  1618.     struct FileInfoBlock* FIBPtr;
  1619.     struct Node*          ListBrowserNodePtr;
  1620.  
  1621.     DateTime.dat_Format  = FORMAT_DOS;
  1622.     DateTime.dat_Flags   = NULL;
  1623.     DateTime.dat_StrDay  = NULL;
  1624.     DateTime.dat_StrDate = datestring;
  1625.     DateTime.dat_StrTime = timestring;
  1626.  
  1627.     if (size.finddup)
  1628.     {   killduplist();
  1629.  
  1630.         lockscreen();
  1631.         firstpen = FindColor
  1632.         (   ScreenPtr->ViewPort.ColorMap,
  1633.             0xDDDDDDDD,
  1634.             0xDDDDDDDD,
  1635.             0xDDDDDDDD,
  1636.             -1
  1637.         );
  1638.         secondpen = FindColor
  1639.         (   ScreenPtr->ViewPort.ColorMap,
  1640.             0xAAAAAAAA,
  1641.             0xAAAAAAAA,
  1642.             0xAAAAAAAA,
  1643.            -1
  1644.         );
  1645.         unlockscreen();
  1646.         currentpen = firstpen;
  1647.  
  1648.         if (shared.log)
  1649.         {   strcpy(logstring, "Duplicates found:\n");
  1650.             writeline();
  1651.         }
  1652.  
  1653.         /* Now we iterate through the complete file list (last entry index
  1654.         of files). For each file (i), we look at every other file (j). As
  1655.         soon as we find a matching file, we add the file (i) to the
  1656.         duplicate file list. */
  1657.  
  1658.         SetGadgetAttrs
  1659.         (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1660.             FUELGAUGE_Min,   1,
  1661.             FUELGAUGE_Max,   files,
  1662.             GA_Text,         "Matching duplicates...",
  1663.             TAG_DONE
  1664.         );
  1665.         setbar(files);
  1666.  
  1667.         for (i = 1; i <= files; i++)
  1668.         {   progressbar(i);
  1669.             breakval = ra_checkbreak();
  1670.             if (breakval == 2)
  1671.             {   cleanexit(EXIT_SUCCESS);
  1672.             } elif (breakval == 1)
  1673.             {   status = STATUS_STOPPING;
  1674.                 SetGadgetAttrs
  1675.                 (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1676.                     GA_Text, "Stopping...",
  1677.                     TAG_END
  1678.                 );
  1679.                 break;
  1680.             }
  1681.             // if the user doesn't want us to stop...
  1682.             for (j = 1; j <= files; j++)
  1683.             {   if (i != j && !stricmp(FilenamePtr[i], FilenamePtr[j]))
  1684.                 {   found = TRUE; // we found (at least one) match
  1685.                     IsDup[i] = TRUE;
  1686.                     break;
  1687.         }   }   }
  1688.  
  1689.         if (status == STATUS_BUSY)
  1690.         {   SetGadgetAttrs
  1691.             (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1692.                 FUELGAUGE_Min,   1,
  1693.                 FUELGAUGE_Max,   files,
  1694.                 GA_Text,         "Condensing duplicates...",
  1695.                 TAG_END
  1696.             );
  1697.             setbar(files);
  1698.  
  1699.             // condense list
  1700.             dups = 0;
  1701.             for (i = 1; i <= files; i++)
  1702.             {   progressbar(i);
  1703.  
  1704.                 if (IsDup[i])
  1705.                 {   dups++;
  1706.                     FilenamePtr[dups] = FilenamePtr[i];
  1707.                     PathnamePtr[dups] = PathnamePtr[i];
  1708.                     IsDup[dups]       = IsDup[i];
  1709.                 } else
  1710.                 {   FreeVec(PathnamePtr[i]);
  1711.                     FreeVec(FilenamePtr[i]);
  1712.             }   }
  1713.  
  1714.             SetGadgetAttrs
  1715.             (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1716.                 FUELGAUGE_Min,   2,
  1717.                 FUELGAUGE_Max,   dups,
  1718.                 GA_Text,         "Sorting duplicates...",
  1719.                 TAG_END
  1720.             );
  1721.             setbar(dups);
  1722.  
  1723.             // bubble sort routine
  1724.             for (i = dups; i >= 2; i--)
  1725.             {   progressbar(dups - i);
  1726.                 for (j = 2; j <= i; j++)
  1727.                 {   if (stricmp(FilenamePtr[j - 1], FilenamePtr[j]) > 0)
  1728.                     {   // swap them
  1729.                         tempPathnamePtr     = PathnamePtr[j - 1];
  1730.                         PathnamePtr[j - 1]  = PathnamePtr[j];
  1731.                         PathnamePtr[j]      = tempPathnamePtr;
  1732.                         tempFilenamePtr     = FilenamePtr[j - 1];
  1733.                         FilenamePtr[j - 1]  = FilenamePtr[j];
  1734.                         FilenamePtr[j]      = tempFilenamePtr;
  1735.             }   }   }
  1736.  
  1737.             SetGadgetAttrs
  1738.             (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1739.                 FUELGAUGE_Min,   0,
  1740.                 FUELGAUGE_Max,   dups,
  1741.                 FUELGAUGE_Level, 0,
  1742.                 GA_Text,         "Examining duplicates...",
  1743.                 TAG_END
  1744.             );
  1745.             setbar(dups);
  1746.  
  1747.             NewList(&DupList);
  1748.  
  1749.             for (i = 1; i <= dups; i++)
  1750.             {   progressbar(i);
  1751.                 breakval = ra_checkbreak();
  1752.                 if (breakval == 2)
  1753.                 {   cleanexit(EXIT_SUCCESS);
  1754.                 } elif (breakval == 1)
  1755.                 {   status = STATUS_STOPPING;
  1756.                     SetGadgetAttrs
  1757.                     (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  1758.                         GA_Text,         "Stopping...",
  1759.                         TAG_END
  1760.                     );
  1761.                     break;
  1762.                 }
  1763.  
  1764.                 // begin creating the line of text
  1765.                 if (PathnamePtr[i][0] == '/')
  1766.                 {   strcpy(liststring[0], &(PathnamePtr[i][1]));
  1767.                 } else
  1768.                 {   strcpy(liststring[0], PathnamePtr[i]);
  1769.                 }
  1770.                 strcpy(logstring, " ");
  1771.                 strcat(logstring, liststring[0]);
  1772.                 if (strlen(logstring) > TRUNCATE)
  1773.                 {   *(logstring + TRUNCATE) = 0; // truncate
  1774.                 }
  1775.                 length = TRUNCATE + 2 - strlen(logstring);
  1776.                 if (length >= 1)
  1777.                 {   for (k = 1; k <= length; k++)
  1778.                     {   strcat(logstring, " ");
  1779.                 }   }
  1780.  
  1781.                 strcpy(pathnamestring, size.path);
  1782.                 if (PathnamePtr[i][0] != '/')
  1783.                 {   if (!AddPart(pathnamestring, PathnamePtr[i], 256))
  1784.                     {   rq("AddPart() failed!");
  1785.                 }   }
  1786.                 else
  1787.                 {   if (!AddPart(pathnamestring, &PathnamePtr[i][1], 256))
  1788.                     {   rq("AddPart() failed!");
  1789.                 }   }
  1790.                 success = FALSE;
  1791.  
  1792.                 // now get size and date of file
  1793.                 if (DirHandle  = (BPTR) Lock(pathnamestring, ACCESS_READ))
  1794.                 {   if (FIBPtr =        AllocDosObject(DOS_FIB, NULL))
  1795.                     {   success = TRUE;
  1796.  
  1797.                         if (Examine(DirHandle, FIBPtr))
  1798.                         {   comma(FIBPtr->fib_Size);
  1799.                             strcat(logstring, commastring);
  1800.                             for (j = 0; j <= 13; j++)
  1801.                             {   if (commastring[j] != ' ')
  1802.                                 {   where = j; // where is index of first non-space character
  1803.                                     break;
  1804.                             }   }
  1805.                             strcpy(liststring[1], &commastring[where]);
  1806.                         } else
  1807.                         {   strcpy(liststring[1], "?");
  1808.                             strcat(logstring, "            ?");
  1809.                         }
  1810.                         strcat(logstring, " ");
  1811.  
  1812.                         DateTime.dat_Stamp   = FIBPtr->fib_Date;
  1813.                         if (DateToStr(&DateTime))
  1814.                         {   strcpy(liststring[2], datestring);
  1815.                             strcpy(liststring[3], timestring);
  1816.                         } else
  1817.                         {   strcpy(liststring[2], "        ?");
  1818.                             strcpy(liststring[3], "        ?");
  1819.                         }
  1820.                         strcat(logstring, liststring[2]);
  1821.                         strcat(logstring, " ");
  1822.                         strcat(logstring, liststring[3]);
  1823.                         strcat(logstring, " ");
  1824.  
  1825.                         FreeDosObject(DOS_FIB, FIBPtr);
  1826.                         // FIBPtr = NULL;
  1827.                         UnLock(DirHandle);
  1828.                         DirHandle = NULL;
  1829.                 }   }
  1830.  
  1831.                 if (!success)
  1832.                 {   strcpy(liststring[1], "            ?");
  1833.                     strcpy(liststring[2], "            ?");
  1834.                     strcpy(liststring[3], "            ?");
  1835.                     strcat(logstring, liststring[1]);
  1836.                     strcat(logstring, " ");
  1837.                     strcat(logstring, liststring[2]);
  1838.                     strcat(logstring, " ");
  1839.                     strcat(logstring, liststring[3]);
  1840.                     strcat(logstring, " ");
  1841.                 }
  1842.                 // now get the file version
  1843.                 if (ram)
  1844.                 {   strcpy(commandstring, "RAM:Version ");
  1845.                 } else
  1846.                 {   strcpy(commandstring, "Version ");
  1847.                 }
  1848.                 strcat(commandstring, pathnamestring);
  1849.                 strcat(commandstring, " >T:ReportPlus.temp");
  1850.                 if (!SystemTags(commandstring, SYS_Output, Open("NIL:", MODE_NEWFILE), TAG_DONE))
  1851.                 {   readordie("T:ReportPlus.temp");
  1852.                     for (k = 0; k <= VLONGFIELD; k++)
  1853.                     {   if (IOBuffer[k] == LF)
  1854.                         {   IOBuffer[k] = 0;
  1855.                             break;
  1856.                     }   }
  1857.                     strcpy(liststring[4], IOBuffer);
  1858.                 } else
  1859.                 {   strcpy(liststring[4], "?");
  1860.                 }
  1861.                 strcat(logstring, liststring[4]);
  1862.  
  1863.                 if (started)
  1864.                 {   if (stricmp(FilenamePtr[i], lastfilename))
  1865.                     {   if (currentpen == firstpen)
  1866.                         {   currentpen = secondpen;
  1867.                         } else currentpen = firstpen;
  1868.                 }   }
  1869.                 else started = TRUE;
  1870.  
  1871.                 if (!(ListBrowserNodePtr = AllocListBrowserNode
  1872.                 (   5,                       // columns,
  1873.                     LBNA_Column,             0,
  1874.                         LBNA_Flags,          LBFLG_CUSTOMPENS,
  1875.                         LBNCA_FGPen,         BLACK,
  1876.                         LBNCA_BGPen,         currentpen,
  1877.                         LBNCA_CopyText,      TRUE,
  1878.                         LBNCA_Text,          liststring[0],
  1879.                     LBNA_Column,             1,
  1880.                         LBNA_Flags,          LBFLG_CUSTOMPENS,
  1881.                         LBNCA_FGPen,         BLACK,
  1882.                         LBNCA_BGPen,         currentpen,
  1883.                         LBNCA_CopyText,      TRUE,
  1884.                         LBNCA_Text,          liststring[1],
  1885.                         LBNCA_Justification, LCJ_RIGHT,
  1886.                     LBNA_Column,             2,
  1887.                         LBNA_Flags,          LBFLG_CUSTOMPENS,
  1888.                         LBNCA_FGPen,         BLACK,
  1889.                         LBNCA_BGPen,         currentpen,
  1890.                         LBNCA_CopyText,      TRUE,
  1891.                         LBNCA_Text,          liststring[2],
  1892.                     LBNA_Column,             3,
  1893.                         LBNA_Flags,          LBFLG_CUSTOMPENS,
  1894.                         LBNCA_FGPen,         BLACK,
  1895.                         LBNCA_BGPen,         currentpen,
  1896.                         LBNCA_CopyText,      TRUE,
  1897.                         LBNCA_Text,          liststring[3],
  1898.                     LBNA_Column,             4,
  1899.                         LBNA_Flags,          LBFLG_CUSTOMPENS,
  1900.                         LBNCA_FGPen,         BLACK,
  1901.                         LBNCA_BGPen,         currentpen,
  1902.                         LBNCA_CopyText,      TRUE,
  1903.                         LBNCA_Text,          liststring[4],
  1904.                     TAG_END
  1905.                 )))
  1906.                 {   rq("Can't create ReAction listbrowser.gadget node(s)!");
  1907.                 }
  1908.                 AddTail(&DupList, ListBrowserNodePtr); // AddTail() has no return code
  1909.                 DupNodes = TRUE;
  1910.                 strcpy(lastfilename, FilenamePtr[i]);
  1911.                 
  1912.                 if (shared.log) // write to logfile
  1913.                 {   strcat(logstring, "\n");
  1914.                     writeline();
  1915.             }   }
  1916.             for (i = 1; i <= dups; i++)
  1917.             {   FreeVec(PathnamePtr[i]);
  1918.                 FreeVec(FilenamePtr[i]);
  1919.         }   }
  1920.         else
  1921.         {   for (i = 1; i <= files; i++)
  1922.             {   FreeVec(PathnamePtr[i]);
  1923.                 FreeVec(FilenamePtr[i]);
  1924.         }   }
  1925.  
  1926.         files = 0;
  1927.         FreeVec(IsDup);
  1928.         IsDup = NULL;
  1929.         FreeVec(PathnamePtr);
  1930.         PathnamePtr = NULL;
  1931.         FreeVec(FilenamePtr);
  1932.         FilenamePtr = NULL;
  1933.         DeleteFile("T:ReportPlus.temp"); /* returns FALSE for failure */
  1934.  
  1935.         if (shared.log)
  1936.         {   if (!found)
  1937.             {   strcpy(logstring, " None\n");
  1938.             }
  1939.             strcat(logstring, "\n");
  1940.             writeline();
  1941.         }
  1942.  
  1943.         SetGadgetAttrs
  1944.         (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
  1945.             LISTBROWSER_Labels, &DupList,
  1946.             TAG_END
  1947.         );
  1948. }   }
  1949.  
  1950. MODULE void addduplicate(STRPTR path, STRPTR filename)
  1951. {   TEXT tablestring[257];
  1952.  
  1953.     if (size.finddup)
  1954.     {   files++;
  1955.         if (files >= MAX_FILES)
  1956.         {   rq("Too many files!");
  1957.         }
  1958.         IsDup[files] = FALSE;
  1959.  
  1960.         if (!(FilenamePtr[files] = AllocVec(strlen(filename) + 1, NULL)))
  1961.         {   rq("Out of memory!");
  1962.         }
  1963.         strcpy(FilenamePtr[files], filename);
  1964.  
  1965.         strcpy(tablestring, path);
  1966.         if (!AddPart(tablestring, filename, 256))
  1967.         {   rq("AddPart() failed!");
  1968.         }
  1969.         if (!(PathnamePtr[files] = AllocVec(strlen(tablestring) + 1, NULL)))
  1970.         {   rq("Out of memory!");
  1971.         }
  1972.         strcpy(PathnamePtr[files], tablestring);
  1973.  
  1974.     /* size.path   path           filename    PathnamePtr[]           FilenamePtr[]
  1975.        "SYS:Tools" "Commodities/" "AutoPoint" "Commodities/AutoPoint" "AutoPoint" */
  1976. }   }
  1977.  
  1978. MODULE void writeline(void)
  1979. {   /* The file is opened just before the first call to writeline()
  1980.     (when the date, time, etc. is written). Then we keep the file open
  1981.     and do our scan. Then we write out the results a line at a time,
  1982.     then the free and total bytes.
  1983.       If we are also doing duplicates, we now do our thinking,
  1984.     write the results a line at a time, then close the file. */
  1985.  
  1986.     if (FPuts(LogFileHandle, logstring))
  1987.     {   Close(LogFileHandle);
  1988.         LogFileHandle = NULL;
  1989.         rq("Can't append to file!");
  1990. }   }
  1991.  
  1992. MODULE void killsizelist(void)
  1993. {   SetGadgetAttrs
  1994.     (   size_gadgets[GID_8_LB2], MainWindowPtr, NULL,
  1995.         LISTBROWSER_Labels, &EmptySizeList,
  1996.         TAG_END
  1997.     );
  1998.     if (SizeNodes)
  1999.     {   clearreactionlist(&SizeList);
  2000.         SizeNodes = FALSE;
  2001. }   }
  2002.  
  2003. MODULE void killduplist(void)
  2004. {   SetGadgetAttrs
  2005.     (   size_gadgets[GID_8_LB1], MainWindowPtr, NULL,
  2006.         LISTBROWSER_Labels, &EmptyDupList,
  2007.         TAG_END
  2008.     );
  2009.     if (DupNodes)
  2010.     {   clearreactionlist(&DupList);
  2011.         DupNodes = FALSE;
  2012. }   }
  2013.  
  2014. AGLOBAL ULONG Hook8Func(struct Hook *h, VOID *o, VOID *msg)
  2015. {   /* "When the hook is called, the data argument points to the
  2016.     window object and message argument to the IntuiMessage." */
  2017.  
  2018.     UWORD code, qual;
  2019.     ULONG scroll;
  2020.  
  2021.     geta4(); // wait till here before doing anything
  2022.     code = ((struct IntuiMessage *) msg)->Code;
  2023.     qual = ((struct IntuiMessage *) msg)->Qualifier;
  2024.     switch(code)
  2025.     {
  2026.     case SCAN_HELP:
  2027.         if (status == STATUS_READY)
  2028.         {   helpabout();
  2029.         }
  2030.     break;
  2031.     case SCAN_ESCAPE:
  2032.         if (status == STATUS_READY)
  2033.         {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2034.             {   cleanexit(EXIT_SUCCESS);
  2035.             } else page = 0;
  2036.         } else
  2037.         {   // assert(status == STATUS_BUSY);
  2038.             status = STATUS_STOPPING;
  2039.         }
  2040.     break;
  2041.     case SCAN_P:
  2042.         ActivateLayoutGadget(size_gadgets[GID_8_LY1], MainWindowPtr, NULL, (Object) size_gadgets[GID_8_ST1]);
  2043.     break;
  2044.     case SCAN_V:
  2045.         if (status == STATUS_READY)
  2046.         {   if (!(qual & IEQUALIFIER_LSHIFT) && !(qual & IEQUALIFIER_RSHIFT))
  2047.             {   if (size.view < UNITOPTIONS)
  2048.                     size.view++;
  2049.                 else size.view = 0;
  2050.             } else
  2051.             {   if (size.view > 0)
  2052.                     size.view--;
  2053.                 else size.view = UNITOPTIONS;
  2054.             }
  2055.             SetGadgetAttrs
  2056.             (   size_gadgets[GID_8_CH1], MainWindowPtr, NULL,
  2057.                 CHOOSER_Selected, size.view,
  2058.                 TAG_END
  2059.             );
  2060.         }
  2061.     break;
  2062.     case SCAN_PERIOD:
  2063.         pathasl();
  2064.     break;
  2065.     case SCAN_UP:
  2066.         if (qual & IEQUALIFIER_CONTROL)
  2067.         {   scroll = LBP_TOP;
  2068.         } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
  2069.         {   scroll = LBP_PAGEUP;
  2070.         } else scroll = LBP_LINEUP;
  2071.     break;
  2072.     case SCAN_DOWN:
  2073.         if (qual & IEQUALIFIER_CONTROL)
  2074.         {   scroll = LBP_BOTTOM;
  2075.         } elif (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
  2076.         {   scroll = LBP_PAGEDOWN;
  2077.         } else scroll = LBP_LINEDOWN;
  2078.     break;
  2079.     default:
  2080.     break;
  2081.     }
  2082.  
  2083.     if (code == SCAN_UP || code == SCAN_DOWN)
  2084.     {   SetGadgetAttrs
  2085.         (   size_gadgets[GID_8_LB2],      // pointer to gadget
  2086.             MainWindowPtr,                // pointer to window (not window object!)
  2087.             NULL,                         // pointer to requester
  2088.             LISTBROWSER_Position, scroll, // tags
  2089.             TAG_DONE                      // done
  2090.         );
  2091.     }
  2092.  
  2093.     return(1);
  2094. }
  2095.  
  2096. MODULE void ghost(void)
  2097. {   status = STATUS_BUSY;
  2098.  
  2099.     SetGadgetAttrs // "update"
  2100.     (   size_gadgets[GID_8_BU4], MainWindowPtr, NULL,
  2101.         GA_Disabled, TRUE,
  2102.         TAG_END
  2103.     );
  2104.     SetGadgetAttrs // "stop"
  2105.     (   size_gadgets[GID_8_BU5], MainWindowPtr, NULL,
  2106.         GA_Disabled, FALSE,
  2107.         TAG_END
  2108.     );
  2109.     SetGadgetAttrs // "menu"
  2110.     (   size_gadgets[GID_8_BU2], MainWindowPtr, NULL,
  2111.         GA_Disabled, TRUE,
  2112.         TAG_END
  2113.     );
  2114. }
  2115.  
  2116. MODULE void unghost(void)
  2117. {   status = STATUS_READY;
  2118.  
  2119.     SetGadgetAttrs // "menu"
  2120.     (   size_gadgets[GID_8_BU2], MainWindowPtr, NULL,
  2121.         GA_Disabled, FALSE,
  2122.         TAG_END
  2123.     );
  2124.     SetGadgetAttrs // "update"
  2125.     (   size_gadgets[GID_8_BU4], MainWindowPtr, NULL,
  2126.         GA_Disabled, FALSE,
  2127.         TAG_END
  2128.     );
  2129.     SetGadgetAttrs // "stop"
  2130.     (   size_gadgets[GID_8_BU5], MainWindowPtr, NULL,
  2131.         GA_Disabled, TRUE,
  2132.         TAG_END
  2133.     );
  2134. }
  2135.  
  2136. MODULE void scale(ULONG bytes)
  2137. {   if (size.view == 0) // bytes
  2138.     {   comma(bytes);
  2139.     } elif (size.view == 1) // kilobytes
  2140.     {   comma(bytes / 1024);
  2141.     } elif (size.view == 2) // megabytes
  2142.     {   comma(bytes / 1048576);
  2143.     } else
  2144.     {   // assert(size.view == 3); // blocks
  2145.         // assert(size.blocksize); // to avoid division by zero
  2146.         comma(bytes / size.blocksize);
  2147.         if (bytes % size.blocksize)
  2148.         {   comma((bytes / size.blocksize) + 1);
  2149.         } else // exactly on a block boundary
  2150.         {   comma(bytes / size.blocksize);
  2151. }   }   }
  2152.  
  2153. MODULE void setbar(ULONG max)
  2154. {   // Ensure we do no more than BARUPDATES updates of the progress bar.
  2155.  
  2156.     increment = max / BARUPDATES;
  2157.     if (increment == 0)
  2158.     {   increment = 1;
  2159. }   }
  2160.  
  2161. MODULE void __inline progressbar(ULONG level)
  2162. {   if (!(level % increment))
  2163.     {   SetGadgetAttrs
  2164.         (   size_gadgets[GID_8_FG1], MainWindowPtr, NULL,
  2165.             FUELGAUGE_Level, level,
  2166.             TAG_END
  2167.         );
  2168. }   }
  2169.  
  2170.